package com.bw.skinfilecreator.utils;

import org.eclipse.jgit.api.CleanCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;

import java.io.*;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.util.Formatter;
import java.util.Set;
import java.util.function.Consumer;

public class GitUtils {
    private volatile static GitUtils utils;
    private XxSKinUtil.UtilConfigProvider utilConfigProvider;

    private GitUtils() {
    }

    public static GitUtils getInstance() {
        if (utils == null) {
            synchronized (GitUtils.class) {
                if (utils == null) {
                    utils = new GitUtils();
                }
            }
        }
        return utils;
    }

    private XxSKinUtil.UtilConfigProvider getUtilConfigProvider() {
        return utilConfigProvider;
    }

    public void setUtilConfigProvider(XxSKinUtil.UtilConfigProvider utilConfigProvider) {
        this.utilConfigProvider = utilConfigProvider;
    }

    /**
     * 执行Git命令
     *
     * @param function
     */
    private void exeGitCMD(Consumer<Git> function) {
        try {
            // 定义Git仓库路径
            String repoPath = getUtilConfigProvider().getProject().getGitProjectPath();

            // 打开现有的仓库
            FileRepositoryBuilder builder = new FileRepositoryBuilder();
            try (Repository repository = builder.setGitDir(Paths.get(repoPath, ".git").toFile()).readEnvironment().findGitDir().build()) {

                // 创建Git对象
                try (Git git = new Git(repository)) {
                    function.accept(git);
                }
            }
        } catch (IOException e) {
            getUtilConfigProvider().onLog("执行Gig命令时出错: " + e.getMessage());
            e.printStackTrace();
        }
    }

    public void switchBranch() {
        exeGitCMD(git -> {
            // 验证当前分支
            String currentBranch = null;
            try {
                currentBranch = git.getRepository().getBranch();
                getUtilConfigProvider().onLog("当前分支是: " + currentBranch);
                // 要切换到的分支名称
                String branchName = getUtilConfigProvider().getProject().getBranchName();
                //如果当前分支和目标分支一致,则跳过切换分支缓降
                if (currentBranch.equals(branchName)) return;
                // 执行分支切换操作
                git.checkout().setName(branchName).call();

                getUtilConfigProvider().onLog("切换到分支: " + branchName);
            } catch (IOException | GitAPIException e) {
                getUtilConfigProvider().onLog("切换分支时出错: " + e.getMessage());
                e.printStackTrace();
            }
        });
    }


    /**
     * Git添加所有变更
     */
    public void addSkinFile() {
        exeGitCMD(git -> {
            try {
                git.add().addFilepattern(".").call();
                getUtilConfigProvider().onLog("添加皮肤包变更完成");
            } catch (GitAPIException e) {
                getUtilConfigProvider().onLog("添加皮肤包变更失败");
                e.printStackTrace();
            }
        });
    }

    /**
     * 提交变更
     *
     * @param tag
     */
    public void commit(String tag) {
        exeGitCMD(git -> {
            try {
                // 生成 Change-Id
                String changeId = generateChangeId(git);

                String stringBuilder = "fix(skinbags)" + "更新" + tag + "皮肤包" + "\n\n" + "Test:无需关注" + "\n\n" +
                        // 拼接提交消息（原消息 + Change-Id）
                        "Change-Id: " + changeId;

                // 执行提交
                RevCommit commit = git.commit().setMessage(stringBuilder).call();

                getUtilConfigProvider().onLog("" + "Commit ID: " + commit.name());
                getUtilConfigProvider().onLog("" + "Change-Id: " + changeId);

                getUtilConfigProvider().onLog("提交变更完成");
            } catch (Exception e) {
                getUtilConfigProvider().onLog("提交变更完成失败");
                getUtilConfigProvider().onLoadingEnd();
                e.printStackTrace();
            }
        });
    }

    // 生成符合 Gerrit 格式的 Change-Id
    public String generateChangeId(Git git) throws Exception {
        // 获取最近一次提交（用于生成基于历史的哈希）
        RevCommit headCommit = getHeadCommit(git);

        // 构建用于生成哈希的内容（可根据需要调整）
        String content = "";
        if (headCommit != null) {
            content += headCommit.name() + "\n"; // 最近提交的 SHA
        }
        // 可添加更多内容（如工作区变更、作者信息等）

        // 生成 SHA-1 哈希
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] hash = md.digest(content.getBytes());

        // 转换为 40 位十六进制字符串，前缀加 "I"
        try (Formatter formatter = new Formatter()) {
            for (byte b : hash) {
                formatter.format("%02x", b);
            }
            return "I" + formatter;
        }
    }

    // 获取当前分支最近一次提交
    private RevCommit getHeadCommit(Git git) throws Exception {
        ObjectId head = git.getRepository().resolve("HEAD");
        if (head == null) {
            return null; // 空仓库，无提交
        }
        try (RevWalk walk = new RevWalk(git.getRepository())) {
            return walk.parseCommit(head);
        }
    }

    /**
     * 撤销工作区中指定文件的未提交修改
     */
    public void restoreUncommittedChanges() {
        exeGitCMD(git -> {
            try {
                git.checkout()
                        .setAllPaths(true)
                        .call();
                getUtilConfigProvider().onLog("已恢复 daytime_mode 到最近提交状态");
            } catch (Exception e) {
                e.printStackTrace();
                getUtilConfigProvider().onLog("恢复失败");
            }

        });
    }

    /**
     * 清理工作区中未跟踪的文件和目录
     */
    public void cleanWorkingDirectory() {

        exeGitCMD(git -> {

            try {
                // 执行清理并返回被清理的路径集合
                Set<String> cleanedPaths = null;

                CleanCommand cleanCommand = git.clean();

                // 配置清理参数
                cleanCommand.setCleanDirectories(true); // 清理未跟踪的目录
                cleanCommand.setIgnore(true); // 默认为 false，若为 true 则忽略 .gitignore 中的文件

                cleanedPaths = cleanCommand.call();

                if (!cleanedPaths.isEmpty()) {
                    getUtilConfigProvider().onLog("已清理以下文件/目录：");

                    for (String path : cleanedPaths) {
                        getUtilConfigProvider().onLog("- " + path);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                getUtilConfigProvider().onLog("清理失败");
            }
        });

    }


    /**
     * 执行CMD命令
     *
     * @param cmd
     * @param callBack
     */
    private void exeCmd(File cmd, Consumer<Boolean> callBack) {
        try {
            Process process = Runtime.getRuntime().exec(cmd.getAbsolutePath());
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); // 获取命令输出流
            String line;
            while ((line = reader.readLine()) != null) { // 逐行读取输出结果
                getUtilConfigProvider().onLog(line);
            }
            reader.close();
            int exitCode = process.waitFor(); // 等待命令执行完成并获取退出码

            //删除命令文件
            if (cmd != null) {
                FileUtils.delete(cmd);
            }

            // 处理错误流（如果有错误输出）
            if (exitCode != 0) {
                InputStream errorStream = process.getErrorStream();
                BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream));
                while ((line = errorReader.readLine()) != null) {
                    getUtilConfigProvider().onLog(line);
                }
                callBack.accept(false);
            } else {
                callBack.accept(true);
            }
        } catch (Exception e) {
            //删除命令文件
            if (cmd != null) {
                FileUtils.delete(cmd);
            }
            e.printStackTrace();
            getUtilConfigProvider().onLog(e.getMessage());
            callBack.accept(false);
        }
    }

    /**
     * 推送提交到远端
     */
    public void push() {
        exeGitCMD(git -> exeCmd(createPushCmd(), aBoolean -> {
            getUtilConfigProvider().onLog("推送到远端" + (aBoolean ? "完成" : "失败"));
            getUtilConfigProvider().onLoadingEnd();
        }));
    }

    /**
     * 获取命令文件路径
     *
     * @return
     */
    private String getGitCmdPath() {
        return getUtilConfigProvider().getProject().getGitProjectPath() + "\\daytime_mode";
    }

    /**
     * 创建推送命令文件
     */
    private File createPushCmd() {
        File file = new File(getGitCmdPath() + "\\push.cmd");
        if (!file.exists()) {
            try {
                file.createNewFile();
                //写命令进入
                PrintWriter printWriter = new PrintWriter(file);
                printWriter.append("cd " + file.getParent());
                printWriter.append("\n");
                printWriter.append("git push origin HEAD:refs/for/" + utilConfigProvider.getProject().getBranchName());
                printWriter.flush();
                printWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
                getUtilConfigProvider().onLog(e.getMessage());
            }
        }
        return file;
    }

    /**
     * 创建拉取命令文件
     */
    private File createPullCmd() {
        File file = new File(getGitCmdPath() + "\\pull.cmd");
        if (!file.exists()) {
            try {
                file.createNewFile();
                //写命令进入
                PrintWriter printWriter = new PrintWriter(file);
                printWriter.append("cd " + file.getParent());
                printWriter.append("\n");
                printWriter.append("git pull");
                printWriter.flush();
                printWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
                getUtilConfigProvider().onLog(e.getMessage());
            }
        }
        return file;
    }

    public void pull() {
        exeGitCMD(git -> exeCmd(createPullCmd(), aBoolean -> getUtilConfigProvider().onLog("拉取远端变更" + (aBoolean ? "完成" : "失败"))));
    }
}
